import json
import logging
import time
from django.utils.deprecation import MiddlewareMixin
from system.models import SysOperLog, SysMenu
from django.contrib.auth import get_user_model

logger = logging.getLogger('ywadmin')

# 自定义JSON编码器，处理不可序列化的类型
class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, set):
            return list(obj)  # 将集合转换为列表
        return super().default(obj)

class AuditMiddleware(MiddlewareMixin):
    """审计日志中间件"""
    
    def __init__(self, get_response):
        self.get_response = get_response
        # 不需要记录日志的路径
        self.exclude_paths = [
            # '/api/login',
            # '/api/logout',
            '/system/oper_log/',
            '/static/',  # 排除配置相关API
            '/media/'
        ]
        
    def process_request(self, request):
        # 记录请求开始时间
        request.audit_start_time = time.time()
        return None
        
    def process_response(self, request, response):
        # 排除不需要记录的路径
        path = request.path
        if any(path.startswith(exclude) for exclude in self.exclude_paths):
            return response
            
        # 只记录API请求
        # if not path.startswith('/api/'):
        #     return response
            
        try:
            # 获取请求信息
            method = request.method
            url = request.get_full_path()
            ip = self.get_client_ip(request)
            
            # 获取用户信息
            username = '未登录用户'
            if hasattr(request, 'user') and request.user.is_authenticated:
                username = request.user.username
            
            # 获取请求参数
            if method in ['POST', 'PUT', 'PATCH']:
                if request.content_type == 'application/json':
                    try:
                        param = json.loads(request.body) if request.body else {}
                    except Exception as e:
                        param = {'error': "无法解析JSON数据,"+str(e)}
                else:
                    param = request.POST.dict()
            else:
                param = request.GET.dict()
                
            # 处理敏感信息
            if 'password' in param:
                param['password'] = '******'
                
            # 获取响应结果
            if hasattr(response, 'data'):
                result = response.data
            else:
                result = {'status': response.status_code}
                
            # 判断操作状态
            status = 0 if 200 <= response.status_code < 400 else 1
            
            # 获取业务类型
            business_type = self.get_business_type(method, path)
            
            # 获取模块标题
            title = self.get_module_title(path)
            
            # 创建日志记录
            SysOperLog.objects.create(
                title=title,
                business_type=business_type,
                method=method,
                request_method=request.META.get('PATH_INFO', ''),
                operator_type=1,  # 默认后台用户
                oper_name=username,
                oper_url=url,
                oper_ip=ip,
                oper_param=json.dumps(param, ensure_ascii=False),
                json_result=json.dumps(result, ensure_ascii=False, cls=CustomJSONEncoder) if not isinstance(result, str) else result,
                status=status,
                error_msg=None if status == 0 else str(result),
            )
            
        except Exception as e:
            logger.error(f"记录审计日志失败: {str(e)}")
            
        return response
        
    def get_client_ip(self, request):
        """获取客户端IP"""
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR', '')
        return ip
        
    def get_business_type(self, method, path):
        """根据请求方法和路径判断业务类型"""
        if method == 'POST':
            return 1  # 新增
        elif method in ['PUT', 'PATCH']:
            return 2  # 修改
        elif method == 'DELETE':
            return 3  # 删除
        else:
            return 0  # 其他
            
    def get_module_title(self, path):
        """根据路径获取模块标题"""
        paths = path.split('/')
        if len(paths) > 3:
            _path = '/'.join(paths[:3])
        else:
            _path = '/'.join(paths[:2])
        menu = SysMenu.objects.filter(path=_path).first()
        if menu:
            return menu.name
        else:
            return '其他模块'